/**
 * Copyright (C) 2009+ Spark Zheng
 *
 * @file        src/prot_types.h 
 * @date        2010-04-24
 * @brief	general_network
 * @see         dserver
 * @history
 */

#ifndef _DSERVER_PROT_TYPES_H
#define _DSERVER_PROT_TYPES_H

#ifdef __cplusplus
extern "C" {
#endif

#include <unistd.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/sendfile.h>
#include <sys/mman.h>
#include <poll.h>
#include <fcntl.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <event.h>

#include <limits.h>
#include <stdint.h>

#ifndef POLLRDNORM
#define	POLLRDNORM	POLLIN
#endif
#ifndef POLLWRNORM
#define	POLLWRNORM	POLLOUT
#endif

/* DSERVER MAGIC NUMBER */
#ifndef DSERVER_MAGIC
#define DSERVER_MAGIC		0x5178
#endif

/* iovec max length */
#ifndef DSERVER_IOV_MAX
#define DSERVER_IOV_MAX		1 * 1024U
#endif

/* global backlog len */
#ifndef DSERVER_BACKLOG_LEN
#define DSERVER_BACKLOG_LEN	8 * 1024U
#endif

/* global buffer size */
#ifndef DSERVER_RX_BUFLEN
#define DSERVER_RX_BUFLEN	8 * 1024U
#endif
/* global max send buffer size */
#ifndef DSERVER_TX_BUFLEN
#define DSERVER_TX_BUFLEN	32 * 1024U
#endif

/* tcp buffer size */
#ifndef DSERVER_TCP_RX_BUFLEN
#define DSERVER_TCP_RX_BUFLEN	16 * 1024U
#endif
/* tcp max send buffer size */
#ifndef DSERVER_TCP_TX_BUFLEN
#define DSERVER_TCP_TX_BUFLEN	64 * 1024U
#endif

/* udp buffer size */
#ifndef DSERVER_UDP_RX_BUFLEN
#define DSERVER_UDP_RX_BUFLEN	16 * 1024U
#endif
/* udp max send buffer size */
#ifndef DSERVER_UDP_TX_BUFLEN
#define DSERVER_UDP_TX_BUFLEN	16 * 1024U
#endif


/** connection struct for tcp client in connection.h */
typedef struct connection	dserver_conn_t;
/** connection pool for tcp client in connection.h */
typedef struct connection_pool	dserver_cpool_t;

typedef enum {
	PROTOCOL_TCP = 1,
	PROTOCOL_UDP,
	PROTOCOL_SCTP_STREAM,
	PROTOCOL_SCTP_DGRAM,
	PROTOCOL_OTHER
} prot_type_t;

/**
 * PROTOCOL_CONTINUE/PROTOCOL_SUCCESS	TCP: keep the connection
 *					UDP: return ok
 * PROTOCOL_CLOSE			TCP: close the connection
 *					UDP: discard anything and data over
 * PROTOCOL_ERROR			TCP: error and then close the connection
 *					UDP: error
 * PROTOCOL_RESET	only for TCP: cause RESET to client, missing TIME_WAIT
 *			WARNING, use PROTOCOL_RESET, you need to know what you really did 
 * PROTOCOL_IGNORE	for TCP: asynchorinize close connection and data stream
 *			for UDP: asynchorinize close data stream
 *			WARNING, use PROTOCOL_IGNORE, you need to know what you really did,
 *			you may lost the connection, and never get it back
 */
typedef enum {
#ifdef PROTOCOL_SUCCESS
#undef PROTOCOL_SUCCESS
#endif
#define PROTOCOL_SUCCESS	PROTOCOL_CONTINUE
	PROTOCOL_CONTINUE = 0,
	PROTOCOL_CLOSE,
	PROTOCOL_RESET,		/** be validate only for TCP */
	PROTOCOL_IGNORE,	/** be careful when use it */
	PROTOCOL_ERROR
} prot_ret_t;

/** protocol module common callbacks */
typedef struct protocol_ops	prot_ops_t;

/** protocol module session callbacks */
typedef struct session_ops	session_ops_t;

/** protocol module least settings parameters */
typedef struct protocol_setting	prot_setting_t;

/** protocol module least relation parameters */
typedef union protocol_base		prot_base_t;
typedef struct protocol_tcp_base	prot_tcp_base_t;
typedef struct protocol_udp_base	prot_udp_base_t;

/** protocol node */
typedef struct protocol_node	prot_node_t;


/**
 * protocol_ops
 * @brief important structure in protocols, the generate protocol operations
 * -p_server_init: create a server
 * -p_server_exit: destroy a server
 * -p_server_conn_handler: handler of connection arrived
 * -p_server_conn_exit: when connection closed
 * -p_server_data_maker: create data to client
 * -p_server_data_handler: handler of data arrived
 * -p_server_data_exit: finishing of data sending
 */
struct protocol_ops {
	int	(*p_server_init)(const char *domain, in_port_t port, uint16_t timeo);
	int	(*p_server_exit)(int sockfd);
	void	(*p_server_conn_handler)(int fd, short flag, void *arg);
	int	(*p_server_conn_exit)(void *arg);
	void	(*p_server_data_maker)(int fd, short flag, void *arg);
	void	(*p_server_data_handler)(int fd, short flag, void *arg);
	int	(*p_server_data_exit)(int sockfd);
};

/**
 * session_ops
 * @brief important structure in protocol, session operations
 * -p_protocol_load: on protocol loading callback function
 * -p_session_init: session beginning callback function
 * -p_session_worker: session prime working function
 * -p_session_exit: session finishing callback function
 * -p_protocol_unload: on protocol unloading callback function
 */
struct session_ops {
#define PROTOCOL_LOAD_STR	"protocol_on_load"
#define SESSION_INIT_STR	"protocol_session_init"
#define SESSION_WORKER_STR	"protocol_session_worker"
#define SESSION_EXIT_STR	"protocol_session_exit"
#define PROTOCOL_UNLOAD_STR	"protocol_on_unload"
	int		(*p_protocol_load)(const void *settings, prot_node_t *prot);
	int		(*p_session_init)(void *arg);
	prot_ret_t	(*p_session_worker)(void *arg);
	void		(*p_session_exit)(void *arg);
	void		(*p_protocol_unload)(prot_node_t *prot);
};

/**
 * protocol_settings
 * @brief settings each protocol node has
 */
struct protocol_setting {
	prot_type_t	p_type;		/** protocol type */
	char		*p_host;	/** protocol listen host */
	uint16_t	p_port;		/** protocol listen port */
	uint16_t	p_priority;	/** protocol priority for schedule */
	uint16_t	p_timeout;	/** protocol timeout */

	uint8_t		p_custom;	/** whether use custom protocol, default 0 */

	uint8_t		p_tcp_nodelay;	/** tcp protocol nodelay option */
	uint8_t		p_tcp_keepalive;/** tcp protocol keepalive option */

	uint32_t	p_max_rx_buf;	/** protocol max rx bufsize option */
	uint32_t	p_max_tx_buf;	/** protocol max tx bufsize option */
};

/**
 * protocol_tcp_base / protocol_udp_base / protocol_base
 */
struct protocol_tcp_base {
	dserver_cpool_t		*p_main_cpool;	/** cpool for TCP */
};

struct protocol_udp_base {
	dserver_conn_t		*p_main_conn;/** global connection for UDP */
};

#define p_tcp_main_cpool	p_tcp_base.p_main_cpool
#define p_udp_main_conn		p_udp_base.p_main_conn
union protocol_base {
	prot_tcp_base_t		p_tcp_base;	/** tcp base */
	prot_udp_base_t		p_udp_base;	/** udp base */
};

/**
 * protocol_node
 * @brief stands for an protocol
 */
struct protocol_node {
	char			*p_name;	/** protocol uniq name */
	
	prot_setting_t		p_settings;	/** protocol settings */
	int			p_sfd;		/** protocol server fd */
	struct event_base	*p_main_event;	/** protocol based main event */
	struct event		p_event;	/** protocol incoming event */
	prot_ops_t		p_ops;		/** protocol callback functions */
	session_ops_t		s_ops;		/** session callback functions */

	prot_base_t		p_base;		/** protocol base structure */
	
	void			*p_data;	/** protocol data in privacy */
};

/** protocol head when use uncustom protocol */
typedef struct protocol_head prot_head_t;

/**
 * protocol_head
 * @brief dserver protocol header for network-stream
 * 1. __attribute__((packed)) for this structure
 * 2. protocol node didnot set custom flag using the follow package
 *    +-----------+
 *    | head      |    seg
 *    |-----------|  +---------+
 *    | seg(pdata)|  | pipe_id |
 *    | seg(pdata)|  | real    |
 *    | ...       |  | data    |
 *    +-----------+  +---------+
 */
struct protocol_head {
	uint32_t		p_length;	/** whole package length */
	uint16_t		p_magic;	/** dserver magic number + version */
	uint16_t		p_ttl;		/** package ttl */
	uint16_t		p_random;	/** avoid cache request */
	uint32_t		p_checksum;	/** verified information */
} __attribute__((packed));

#ifndef DSERVER_HEAD_SIZE
#define DSERVER_HEAD_SIZE	sizeof(prot_head_t)
#endif


/** PROTOCOL APIs */
static inline prot_type_t dserver_prot_type(prot_node_t *prot)
{
	return prot->p_settings.p_type;
}


/** dserver server framework api */
/**
 * dserver_tcp_conn_receive
 * @brief when tcp server received a connection
 * @param prot protocol-node
 */
extern int dserver_tcp_conn_receive(prot_node_t *prot);

extern int dserver_tcp_data_sendout(dserver_conn_t *conn);

extern int dserver_tcp_data_receive(dserver_conn_t *conn);

extern int dserver_udp_data_sendout(prot_node_t *prot);

extern int dserver_udp_data_receive(prot_node_t *prot);

#ifdef __cplusplus
}
#endif
#endif
